/*
*  Copyright 2019-2020 Zheng Jie
*
*  Licensed under the Apache License, Version 2.0 (the "License");
*  you may not use this file except in compliance with the License.
*  You may obtain a copy of the License at
*
*  http://www.apache.org/licenses/LICENSE-2.0
*
*  Unless required by applicable law or agreed to in writing, software
*  distributed under the License is distributed on an "AS IS" BASIS,
*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*  See the License for the specific language governing permissions and
*  limitations under the License.
*/
package me.zhengjie.modules.biz.service.impl;

import cn.hutool.core.convert.Convert;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import lombok.val;
import me.zhengjie.modules.biz.domain.Announcement;
import me.zhengjie.modules.biz.domain.Lab;
import me.zhengjie.modules.biz.domain.LabBusyTime;
import me.zhengjie.modules.biz.domain.Message;
import me.zhengjie.modules.biz.repository.LabRepository;
import me.zhengjie.modules.biz.repository.MessageRepository;
import me.zhengjie.modules.biz.service.dto.CabinetDto;
import me.zhengjie.modules.system.domain.User;
import me.zhengjie.modules.system.repository.UserRepository;
import me.zhengjie.utils.*;
import lombok.RequiredArgsConstructor;
import me.zhengjie.modules.biz.repository.LabBusyTimeRepository;
import me.zhengjie.modules.biz.service.LabBusyTimeService;
import me.zhengjie.modules.biz.service.dto.LabBusyTimeDto;
import me.zhengjie.modules.biz.service.dto.LabBusyTimeQueryCriteria;
import me.zhengjie.modules.biz.service.mapstruct.LabBusyTimeMapper;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

import java.text.SimpleDateFormat;
import java.util.*;
import java.io.IOException;
import javax.persistence.criteria.Predicate;
import javax.servlet.http.HttpServletResponse;

/**
* @website https://eladmin.vip
* @description 服务实现
* @author Admin
* @date 2023-02-16
**/
@Service
@RequiredArgsConstructor
public class LabBusyTimeServiceImpl implements LabBusyTimeService {

    private final LabBusyTimeRepository labBusyTimeRepository;
    private final LabBusyTimeMapper labBusyTimeMapper;
    private final UserRepository userRepository;
    private final LabRepository labRepository;

    private final MessageRepository messageRepository;

    @Override
    public Map<String,Object> queryAll(LabBusyTimeQueryCriteria criteria, Pageable pageable){
        Page<LabBusyTime> page = labBusyTimeRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder),pageable);
        Page<LabBusyTimeDto> dto = page.map(labBusyTimeMapper::toDto);
        List<LabBusyTimeDto> resultDto = dto.getContent();
        for (LabBusyTimeDto labBusyTimeDto:resultDto) {
            Lab lab = labRepository.findById(labBusyTimeDto.getLabId()).orElseGet(Lab::new);
            labBusyTimeDto.setLabName(lab.getName());
            if (labBusyTimeDto.getUserId() != null) {
                User user = userRepository.findById(Long.valueOf(labBusyTimeDto.getUserId())).orElseGet(User::new);
                labBusyTimeDto.setUserName(user.getUsername());
            }
            if (labBusyTimeDto.getAuditId() != null) {
                User user = userRepository.findById(Long.valueOf(labBusyTimeDto.getAuditId())).orElseGet(User::new);
                labBusyTimeDto.setAuditName(user.getUsername());
            }
        }
        return PageUtil.toPage(dto);
    }

    @Override
    public List<LabBusyTimeDto> queryAll(LabBusyTimeQueryCriteria criteria){
        Specification<LabBusyTime> specification =  (root, query, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();
            if (criteria.getId() != null) {
                predicates.add(criteriaBuilder.equal(root.get("id"),  (criteria.getId())));
            }
            if (criteria.getUserId() != null) {
                predicates.add(criteriaBuilder.equal(root.get("userId"),  criteria.getUserId()));
            }
            if (criteria.getStatus() != null) {
                predicates.add(criteriaBuilder.equal(root.get("status"),  criteria.getStatus()));
            }
            if (criteria.getLabId() != null) {
                predicates.add(criteriaBuilder.equal(root.get("labId"),  criteria.getLabId()));
            }
            predicates.add(criteriaBuilder.equal(root.get("auditId"),  SecurityUtils.getCurrentUserId()));
            return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
        };
        List<LabBusyTime> labBusyTimeList = labBusyTimeRepository.findAll(specification);
        List<LabBusyTimeDto> labBusyTimeDtoList = labBusyTimeMapper.toDto(labBusyTimeList);
        String startTime = null;
        String endTime = null;
        for (LabBusyTimeDto labBusyTimeDto : labBusyTimeDtoList) {
            String date = Convert.toStr(labBusyTimeDto.getDate());
            String times = labBusyTimeDto.getTimes();
            JSONArray array = JSONUtil.parseArray(times);
            for (int i = 0; i < array.size(); i++) {
                startTime = DateUtil.sqlDateToStr(array.getJSONObject(i).getLong("start"), "HH:mm");
                endTime = DateUtil.sqlDateToStr(array.getJSONObject(i).getLong("end"), "HH:mm");
                date = (new StringBuilder()).append(date).append(" ").append(startTime).append("-").append(endTime).toString();
            }
            labBusyTimeDto.setDateStr(date);
            Lab lab = labRepository.findById(labBusyTimeDto.getLabId()).orElseGet(Lab::new);
            labBusyTimeDto.setLabName(lab.getName());
            if (labBusyTimeDto.getUserId() != null) {
                User user = userRepository.findById(Long.valueOf(labBusyTimeDto.getUserId())).orElseGet(User::new);
                labBusyTimeDto.setUserName(user.getUsername());
            }
            if (labBusyTimeDto.getAuditId() != null) {
                User user = userRepository.findById(Long.valueOf(labBusyTimeDto.getAuditId())).orElseGet(User::new);
                labBusyTimeDto.setAuditName(user.getUsername());
            }
        }
        return labBusyTimeDtoList;
    }

    @Override
    @Transactional
    public LabBusyTimeDto findById(Integer id) {
        LabBusyTime labBusyTime = labBusyTimeRepository.findById(id).orElseGet(LabBusyTime::new);
        ValidationUtil.isNull(labBusyTime.getId(),"LabBusyTime","id",id);
        return labBusyTimeMapper.toDto(labBusyTime);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public LabBusyTimeDto create(LabBusyTime resources) {
        resources.setUserId(Convert.toInt(SecurityUtils.getCurrentUserId()));
        resources.setStatus(0);
        resources.setAuditId(1);
        LabBusyTime labBusyTime = labBusyTimeRepository.save(resources);
        Message message = new Message();
        message.setUserId(resources.getAuditId());
        message.setUrl("/lab-appointment/bookingAuth");
        message.setName("实验室预约待审批");
        message.setType(2);
        message.setMapId(labBusyTime.getId());
        message.setMenuId(8);
        message.setIsRead(0);
        message.setIsDeleted(0);
        messageRepository.save(message);
        return labBusyTimeMapper.toDto(labBusyTime);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(LabBusyTime resources) {
        LabBusyTime labBusyTime = labBusyTimeRepository.findById(resources.getId()).orElseGet(LabBusyTime::new);
        ValidationUtil.isNull( labBusyTime.getId(),"LabBusyTime","id",resources.getId());
        labBusyTime.copy(resources);
        labBusyTimeRepository.save(labBusyTime);
    }

    @Override
    public void deleteAll(Integer[] ids) {
        for (Integer id : ids) {
            labBusyTimeRepository.deleteById(id);
        }
    }

    @Override
    public void download(List<LabBusyTimeDto> all, HttpServletResponse response) throws IOException {
        List<Map<String, Object>> list = new ArrayList<>();
        for (LabBusyTimeDto labBusyTime : all) {
            Map<String,Object> map = new LinkedHashMap<>();
            map.put("实验室", labBusyTime.getLabName());
            map.put("预约时间段", labBusyTime.getDateStr());
            map.put("预约人",  labBusyTime.getUserName());
            map.put("审核人",  labBusyTime.getAuditName());
            String status = "";
            switch (labBusyTime.getStatus()) {
                case 0:
                    status = "待审批";
                    break;
                case 1:
                    status = "未通过";
                    break;
                case 2:
                    status = "已通过";
                    break;
                case 3:
                    status = "已取消";
                    break;
            }
            map.put("状态", status);
            list.add(map);
        }
        FileUtil.downloadExcel(list, response);
    }

    @Override
    public List<LabBusyTime> queryLabBookedTimeById(Integer labId) {
        return labBusyTimeRepository.findByLabId(labId);
    }

    @Transactional(rollbackFor = Exception.class)
    public void cancel(Integer id) {
        labBusyTimeRepository.cancel(id);
    }

    @Transactional(rollbackFor = Exception.class)
    public boolean approve(Integer id) {
        LabBusyTime labBusyTime = labBusyTimeRepository.findById(id).orElseGet(LabBusyTime::new);
        JSONArray toBeApproveArray = JSONUtil.parseArray(labBusyTime.getTimes());
        List<LabBusyTime> approvedLists = labBusyTimeRepository.findByDateAndStatus(labBusyTime.getDate(), 2);
        if (approvedLists != null) {
            String approvedTime = null;
            JSONArray array = new JSONArray();
            for (LabBusyTime temp : approvedLists) {
                if (temp.getId() == id) {
                    continue;
                }
                approvedTime = temp.getTimes();
                array = JSONUtil.parseArray(approvedTime);
                if (!(toBeApproveArray.getJSONObject(0).getStr("start").compareTo(array.getJSONObject(0).getStr("end")) > 0 || toBeApproveArray.getJSONObject(0).getStr("end").compareTo(array.getJSONObject(0).getStr("start")) < 0)) {
                    return false;
                }
            }
        }
        labBusyTimeRepository.approve(id);
        Message message = new Message();
        message.setUserId(labBusyTime.getUserId());
        message.setUrl("labAuth");
        message.setName("实验室预约成功");
        message.setType(2);
        message.setMapId(labBusyTime.getId());
        message.setIsRead(0);
        message.setIsDeleted(0);
        messageRepository.save(message);

        return true;
    }

    @Transactional(rollbackFor = Exception.class)
    public void reject(Integer id, String auditNote){
        labBusyTimeRepository.reject(id, auditNote);
        LabBusyTime labBusyTime = labBusyTimeRepository.findById(id).orElseGet(LabBusyTime::new);
        Message message = new Message();
        message.setUserId(labBusyTime.getUserId());
        message.setUrl("labAuth");
        message.setName("实验室预约失败");
        message.setType(2);
        message.setMapId(labBusyTime.getId());
        message.setIsRead(0);
        message.setIsDeleted(0);
        messageRepository.save(message);
    }

    @Override
    public Map<String, Object> queryLabBusyTimeSelf(LabBusyTimeQueryCriteria criteria, Pageable pageable) {
        Specification<LabBusyTime> specification = (Specification<LabBusyTime>) (root, query, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<Predicate>();
            if (criteria.getId() != null) {
                predicates.add(criteriaBuilder.equal(root.get("id"),  (criteria.getId())));
            }
            if (criteria.getUserId() != null) {
                predicates.add(criteriaBuilder.equal(root.get("userId"),  criteria.getUserId()));
            }
            if (criteria.getStatus() != null) {
                predicates.add(criteriaBuilder.equal(root.get("status"),  criteria.getStatus()));
            }
            if (criteria.getLabId() != null) {
                predicates.add(criteriaBuilder.equal(root.get("labId"),  criteria.getLabId()));
            }
            predicates.add(criteriaBuilder.equal(root.get("auditId"),  SecurityUtils.getCurrentUserId()));
            return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
        };
        Page<LabBusyTime> page = labBusyTimeRepository.findAll(specification, pageable);
        Page<LabBusyTimeDto> dto = page.map(labBusyTimeMapper::toDto);
        List<LabBusyTimeDto> resultDto = dto.getContent();
        for (LabBusyTimeDto labBusyTimeDto:resultDto) {
            Lab lab = labRepository.findById(labBusyTimeDto.getLabId()).orElseGet(Lab::new);
            labBusyTimeDto.setLabName(lab.getName());
            if (labBusyTimeDto.getUserId() != null) {
                User user = userRepository.findById(Long.valueOf(labBusyTimeDto.getUserId())).orElseGet(User::new);
                labBusyTimeDto.setUserName(user.getUsername());
            }
            if (labBusyTimeDto.getAuditId() != null) {
                User user = userRepository.findById(Long.valueOf(labBusyTimeDto.getAuditId())).orElseGet(User::new);
                labBusyTimeDto.setAuditName(user.getUsername());
            }
        }
        return PageUtil.toPage(dto);
    }

    @Override
    public Map<String, Object> queryUserAll(LabBusyTimeQueryCriteria criteria, Pageable pageable) {

        Specification<LabBusyTime> specification = (Specification<LabBusyTime>) (root, query, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<Predicate>();
            predicates.add(criteriaBuilder.equal(root.get("userId"),  SecurityUtils.getCurrentUserId()));
            return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
        };
        Page<LabBusyTime> page = labBusyTimeRepository.findAll(specification, pageable);
        Page<LabBusyTimeDto> dto = page.map(labBusyTimeMapper::toDto);
        List<LabBusyTimeDto> resultDto = dto.getContent();
        for (LabBusyTimeDto labBusyTimeDto:resultDto) {
            Lab lab = labRepository.findById(labBusyTimeDto.getLabId()).orElseGet(Lab::new);
            labBusyTimeDto.setLabName(lab.getName());
            if (labBusyTimeDto.getUserId() != null) {
                User user = userRepository.findById(Long.valueOf(labBusyTimeDto.getUserId())).orElseGet(User::new);
                labBusyTimeDto.setUserName(user.getUsername());
            }
            if (labBusyTimeDto.getAuditId() != null) {
                User user = userRepository.findById(Long.valueOf(labBusyTimeDto.getAuditId())).orElseGet(User::new);
                labBusyTimeDto.setAuditName(user.getUsername());
            }
        }
        return PageUtil.toPage(dto);    }
}
